package Wormish.agiletre.se;

import java.util.List;
import java.util.Vector;

import Wormish.agiletre.se.Math.MathHelper;
import Wormish.agiletre.se.Math.Vec2f;
import android.inputmethodservice.Keyboard.Key;
import android.os.Handler;
import android.util.Log;

public abstract class PhysicLaws
{
	public final static int GRAVITY = 200;
	public final static int ROPE_GRAVITY = 100;

	public static void loopUpdateState(PhysicsObject theObject, float dt)
	{	
		if(dt > 0.5f)
			Log.v("Collision", "dt spike!:" + dt);
		
		float friction = 0.05f;
		
		//Update acceleration
		if(theObject.AffectedByGravity)
		{	
			if(theObject.getClass().equals(LocalPlayer.class) && GameThread.InputState.IsKeyRopePressed) 
			{
				theObject.acc.setY(ROPE_GRAVITY); //If hanging in a rope, have a little less gravity
			}
			else
			{
				theObject.acc.setY(GRAVITY);
			}
		}
		
		//Update velocity
		theObject.vel.add(theObject.acc.scale(dt, true));
		
		//Add friction or drag on current velocity
		if(theObject.OnGround)
		{
			theObject.vel.setX(theObject.vel.getX() * (1.0f - friction));
		}
		else
		{
			theObject.vel.setX(theObject.vel.getX() * (1.0f - theObject.Drag));
		}
		
		
		Vec2f[] inValues = {theObject.pos, theObject.vel, theObject.acc};
		
		
		//Collision check with player
		if(!theObject.getClass().equals(LocalPlayer.class) && !theObject.getClass().equals(OpponentPlayer.class)){
			int playerCollision = collisionCheckWithPlayer(inValues,dt);
			if(playerCollision!=0){ //Om kollision med spelare
				theObject.onCollisionWithPlayer(playerCollision);
			}
		}
		
		if(!theObject.getAlive())
		{
			return;
		}
		
		boolean collision = false;
		
		//Clamp velocity
		/*if(theObject.getClass().equals(Worm.class))
		{
			float maxSpeed = 100f;
			theObject.vel.setValues(MathHelper.Clamp(theObject.vel.getX(), -maxSpeed, maxSpeed), MathHelper.Clamp(theObject.vel.getY(), -maxSpeed, maxSpeed));
		}*/
		
		//Calculate next position
		Vec2f currentPosition = theObject.pos;
		Vec2f nextPosition = theObject.pos.add(theObject.vel.scale(dt, true), true);
	
		if(GameThread.mMap.getPixel(currentPosition) == Map.GROUND)
		{
			//Log.d("Collision", "Came from ground into updateStateLoop");
			collision = true;
		}
		
		boolean climbedSlope = false;
		
		//Check x-collision
		if(GameThread.mMap.getPixel(nextPosition.getX(), currentPosition.getY()) == Map.GROUND)
		{
			//Log.d("Collision", "Collision in x");
			collision = true;
			
			if(theObject.getClass().equals(LocalPlayer.class)) // The player can climb slopes but not shots
			{
				//If there is a pixel maxSteepness-pixels above the new x that isn't ground, move y and continue
				int maxSteepness = 8;
				for(int j = 1; j <= maxSteepness; j++)
				{
					if(GameThread.mMap.getPixel(nextPosition.getX(), currentPosition.getY() - j) != Map.GROUND)
					{
						nextPosition.setY(currentPosition.getY() - j); //Found new y
						climbedSlope = true;
						collision = false;
						break;
					}
				}				
			}
			
			if(climbedSlope == false) // We walked into a wall
			{
				//Iterate through the pixels that we have passed
				float dx = Math.abs(nextPosition.getX() - currentPosition.getX());
				
				//If we haven't moved any in x-axis but there's still a collision -> we came from ground
				if(dx > 0)
				{
					int sign = -(int)Math.signum(theObject.vel.getX()); //Get the direction we're walking in
					
					if(sign == 0)
						Log.d("Collision", "Sign failed in x-axis, this shouldn't happen!");
	
					boolean foundNewX = false;
					
					for(int j = 0; j <= dx + 1; j++)
					{
						if(GameThread.mMap.getPixel(nextPosition.getX() + j * sign, currentPosition.getY()) != Map.GROUND)
						{
							nextPosition.setX(nextPosition.getX() + j * sign); //Found new x-coordinate that isn't ground
							foundNewX = true;
							break;
						}
					}
					
					if(foundNewX == false)
					{
						//Log.d("Collision", "Couldn't find new x-position!");
					}
				}
				else
				{
					//Log.d("Collision", "Came from ground into x");
					//Came from ground, push out somehow?
				}
				
				theObject.vel.setX(theObject.vel.getX() * -theObject.Bounce);
				collision = true;
			}
		}
		
		if(GameThread.mMap.getPixel(currentPosition) == Map.GROUND)
		{
			//Log.d("Collision", "currentPosition = GROUND!");
		}
		
		//Collsion y check
		if(GameThread.mMap.getPixel(nextPosition) == Map.GROUND)
		{
			//Log.d("Collision", "Collision in y");
			//Iterate through the pixels we have passed on the y-axis
			float dy = Math.abs(nextPosition.getY() - currentPosition.getY());
			
			if(dy > 0)
			{
				int sign = -(int)Math.signum(theObject.vel.getY()); // Get the direction that we were moving in
				
				if(sign == 0)
					Log.d("Collision", "Sign failed in y-axis, this shouldn't happen!");

				boolean foundNewY = false;
				
				for(int j = 0; j <= dy + 1; j++) //+1 because of float -> integer conversion 
				{
					if(GameThread.mMap.getPixel(nextPosition.getX(), nextPosition.getY() + j * sign) != Map.GROUND)
					{
						nextPosition.setY(nextPosition.getY() + j * sign); //Found new y-coordinate that isn't ground
						//Log.d("Collision", "Found new y!");
						foundNewY = true;
						break;
					}
				}
				
				if(foundNewY == false)
				{
					//Log.d("Collision", "Couldn't find new y, dy: " + dy);
					//nextPosition.setY(currentPosition.getY());
				}
			}
			else
			{
				//Log.d("Collision", "Came from ground, into y-check");
				//If we're on ground but haven't moved anything, then we came from ground
				//Came from ground, push out somehow?
			}
			
			theObject.vel.setY(theObject.vel.getY() * -theObject.Bounce);
			collision = true;
		}
		
		
		
		//Create a line from current xy to target xy and check if there is a ground pixel between them
		int yOffset = 0; //theObject.LineCheckYOffset; // Create the line yOffset pixels above
		Vector<Vec2f> line = CreateLine((int)currentPosition.getX(), (int)(currentPosition.getY() - yOffset), (int)nextPosition.getX(), (int)(nextPosition.getY() - yOffset));
		int lineSize = line.size();
		
		if(GameThread.mMap.getPixel(currentPosition) == Map.GROUND)
		{
			//Log.d("Collision", "CurrentPos is ground -- before bresenham!");
		}
		
		if(lineSize >= 3 && climbedSlope == false) //We only need to check if an object has passed through anything if the line is at least 3px in width
		{
			for(int i = 1; i < lineSize; i++)
			{
				if(GameThread.mMap.getPixel(line.elementAt(i)) == Map.GROUND)
				{
					/*Log.d("Collision", "ZOMG GROUND: " + line.elementAt(i).getX() + ", " + line.elementAt(i).getY());
					
					Log.d("Collision", "Object went through ground");
					Log.d("Collision", "Line size: " + lineSize + " CurrXY: " + (int)currentPosition.getX() + "," + (int)(currentPosition.getY()));
					Log.d("Collision", "NextXY: " + (int)nextPosition.getX() + ", " + (int)(nextPosition.getY()));
					*/
					Vec2f p0 = line.elementAt(i);
					Vec2f p1 = line.elementAt(i-1);
					
					int dx = (int)(p1.getX()-p0.getX());
					int dy = (int)(p1.getY()-p0.getY());
					
					Vec2f fixedPos = line.elementAt(i-1).add(0, 0, true);
					
					if(GameThread.mMap.getPixel(fixedPos) == Map.GROUND)
					{
						nextPosition.setValues(theObject.pos); //temp test
						//Log.d("Collision", "Fixed pos is ground :'(, reset to last position");
					}
					else
					{
						nextPosition.setValues(fixedPos);
						
						if(dx != 0)
							theObject.vel.setX(theObject.vel.getX()*-theObject.Bounce);
						if(dy != 0)
							theObject.vel.setY(theObject.vel.getY()*-theObject.Bounce);
						
						//Log.d("Collision", "Fixed position! " + fixedPos.getX() + ", " + fixedPos.getY() + ", dx: " + dx + ", dy: " + dy);
					}

					collision = true;
					break;
				}
			}
		}
		
		if(GameThread.mMap.getPixel(nextPosition) == Map.GROUND)
		{
			//Log.d("Collision", "Target xy is ground! This shouldn't happen!");
		}
		
		theObject.pos.setValues(nextPosition); //.getX(), (int)nextPosition.getY()); //(int)nextPosition.getX();
		
		//Reset OnGround
		theObject.OnGround = false;
		
		//Check the 5 pixels below theObject to see if there is ground somewhere
		for(int n = 1; n <= 5; n++)
		{
			if(GameThread.mMap.getPixel(theObject.pos.getX(), theObject.pos.getY() + n) == Map.GROUND)
			{
				theObject.OnGround = true;
				break;
			}
		}
		
		if(collision)
		{
			theObject.onCollision(theObject.pos);
		}
	}
	
	//Creates a line from x0,y0 to x1,y1 using the bresenham algorithm
	private static Vector<Vec2f> CreateLine(int x0, int y0, int x1, int y1)
	{
		Vector<Vec2f> line = new Vector<Vec2f>();
		
		int dx = Math.abs(x0 - x1);
		int dy = Math.abs(y0 - y1);
		int sx = -1; int sy = -1;
		int err = dx-dy;
		 
	   if(x0 < x1)
		   sx = 1;
	   if(y0 < y1)
		   sy = 1;
	   
	   while(true)
	   {
		   line.add(new Vec2f(x0, y0));
		   
		   if(x0 == x1 && y0 == y1)
		   {
			   break;
		   }
		   
		   int e2 = err * 2;
		   if(e2 > -dy)
		   {
			   err -= dy;
			   x0 += sx;
		   }
		   if(e2 < dx)
		   {
			   err += dx;
			   y0 += sy;
		   }
	   }

	   return line;
	}
	
	public static int collisionCheckWithPlayer(Vec2f[] inCollisionValues, float dt){
		
		Vec2f posCurr = inCollisionValues[0];
		Vec2f velNext = inCollisionValues[1];
		
		/*if(GameThread.localPlayer.boundingBox.contains((int)posCurr.getX(), (int)posCurr.getY()))
		{
			return 1;
		}
		else if(GameThread.opponentPlayer.boundingBox.contains((int)posCurr.getX(), (int)posCurr.getY()))
		{
			return 2;
		}
		
		return 0;*/
		
		//Determines where the object WANTS to go
		Vec2f posNext = posCurr.add(velNext.scale(dt, true), true);
		
		//Calculates the difference between next and current position
		Vec2f posDiffVector = posNext.subtract(posCurr, true);
		
		//Determines how many cells the object has passed
		float amountOfCellsToLookAt = Math.max(Math.abs(posDiffVector.getX()), Math.abs(posDiffVector.getY()));
		int ceilAmountOfCellsToLookAt = (int)Math.ceil(amountOfCellsToLookAt);
		
		//The function below will initially look and compare values to the current position
		Vec2f posJustLookedAt = new Vec2f(posCurr);
		
		for(int i=1; i<=ceilAmountOfCellsToLookAt; i++)
		{
			//Moves in posDiffVector's direction, one cell at a time
			Vec2f posDiffVectorScaled = posDiffVector.scale((float)i/(float)ceilAmountOfCellsToLookAt,true);
			Vec2f posCurrentlyLookingAt = posCurr.add(posDiffVectorScaled, true);
		    
			Log.v("Testar","Testar krock");
			
			if(GameThread.localPlayer.boundingBox.contains((int)posCurrentlyLookingAt.getX(), (int)posCurrentlyLookingAt.getY())){
				return 1;
			}
			else if(GameThread.opponentPlayer != null && GameThread.opponentPlayer.boundingBox.contains((int)posCurrentlyLookingAt.getX(), (int)posCurrentlyLookingAt.getY())){
				return 2;
			}
		}
		return 0;
	}
}
